﻿using SketchUpAPI.NET.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SketchUpAPI.NET.Geometry
{
    /// <summary>
    /// 圆柱体
    /// </summary>
    /// <remarks>该类型并非 SketchUpAPI 中的类型，而是用于辅助创建模型</remarks>
    public class Cylinder : IGeometry
    {
        /// <summary>
        /// 底部圆心
        /// </summary>
        public Point3D BasePoint { get; set; }

        private Vector3D _normal = Vector3D.UnitZ;
        /// <summary>
        /// 高度的方向
        /// </summary>
        /// <remarks>自动保存单位向量</remarks>
        public Vector3D Normal
        {
            get
            {
                return _normal;
            }
            set
            {
                _normal = value.Unit;
            }
        }

        /// <summary>
        /// 高度
        /// </summary>
        public double Height { get; set; }

        /// <summary>
        /// 半径
        /// </summary>
        public double Radius { get; set; }

        /// <summary>
        /// 分段数
        /// </summary>
        public int SegmentCount { get; set; } = 30;

        /// <summary>
        /// 创建一个圆柱体
        /// </summary>
        /// <param name="basePoint">底部中心点</param>
        /// <param name="radius">半径</param>
        /// <param name="height">高度</param>
        /// <exception cref="ArgumentOutOfRangeException">当<paramref name="length"/>、<paramref name="width"/>、<paramref name="height"/> 不大于0 时抛出异常</exception>
        public Cylinder(Point3D basePoint, double radius, double height)
        {
            if (radius == 0 || height == 0) throw new ArgumentOutOfRangeException("半径和高度必须大于0!");

            BasePoint = basePoint;
            Radius = radius;
            Height = height;
        }

        /// <summary>
        /// 创建几何体
        /// </summary>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException">半径或高度小于等于0是抛出该异常</exception>
        public GeometryInput ToGeometry()
        {
            if (Radius <= 0 || Height <= 0) throw new ArgumentOutOfRangeException("半径或高度不能为0");

            Vector3D VectorX = GetX();
            GeometryInput gi = new GeometryInput();

            // 底面
            Point3D bot = BasePoint + VectorX * Radius;
            int botCircleS = gi.AddVertex(bot); // 点索引：0

            // 全部顶点的索引：0 - 29（假如SegmentCount为30）；底面朝上是为了方便创建侧面（顶面和底面的顶点索引可以一一对应）
            gi.AddArc(botCircleS, botCircleS, BasePoint, Normal, SegmentCount, out _);
            gi.AddFace(botCircleS, SegmentCount);
            gi.FaceSetReverse(0, true); // 将底面方向朝下

            // 顶面
            Point3D topCenter = BasePoint + Normal * Height;
            Point3D top = bot + Normal * Height;
            int topCircleS = gi.AddVertex(top); // 点索引：30（假如SegmentCount为30）
            gi.AddArc(topCircleS, topCircleS, topCenter, Normal, SegmentCount, out _); // 全部顶点索引：30 - 59（假如SegmentCount为30）
            gi.AddFace(topCircleS, SegmentCount);

            // 侧面
            for (int i = 0; i < SegmentCount; i++)
            {
                List<int> ver = new List<int>();
                if (i < SegmentCount - 1)
                {
                    ver.Add(botCircleS + i);
                    ver.Add(botCircleS + i + 1);
                    ver.Add(topCircleS + i + 1);
                    ver.Add(topCircleS + i);
                }
                else
                {
                    // 最后一个面
                    ver.Add(botCircleS + i);
                    ver.Add(botCircleS);
                    ver.Add(topCircleS);
                    ver.Add(topCircleS + i);
                }

                LoopInput loop = new LoopInput();
                ver.ForEach(v => loop.AddVertex(v));
                loop.SetEdgeSmooth(1, true);
                loop.SetEdgeSoft(1, true);
                loop.SetEdgeSmooth(3, true);
                loop.SetEdgeSoft(3, true);

                gi.AddFace(loop);
            }

            return gi;
        }

        /// <summary>
        /// 获取一个任意垂直Normal的向量（Y 为0）
        /// </summary>
        /// <returns></returns>
        private Vector3D GetX()
        {
            if (Normal.X == 0) return Vector3D.UnitX;

            double x = -Normal.Z / Normal.X; // 连个向量垂直，则点乘为0；假设 z 为1，y 为0，即可求 x

            return new Vector3D(x, 0, 1).Unit;
        }
    }
}